<template>
  <div>
    <div class="breadcrumb">
      <div
        v-for="(item, index) of breadcrumb"
        :key="item.key"
        @click="pop(index)"
        name="keyName"
      >
        <span>{{ item.key }}</span>
        <i v-if="index !== breadcrumb.length - 1" class="el-icon-arrow-right" style="color: #3d3d3d"></i>
      </div>
      <span v-if="change" style="color: var(--theme-color);">*</span>
      <div v-if="isDir" class="create" @click="createKey()">create</div>
    </div>
    <div class="box">
      <div v-if="isDir" v-loading="loading">
        <div
          v-for="item of list"
          :key="item._id"
          @click="push(item)"
          class="item"
        >
          <i :class="item.type"></i>
          <span>{{ item.key }}</span>
          <i @click.stop="delKey(item)" class="el-icon-delete del"></i>
        </div>
        <div class="noData" v-if="list.length === 0">暂无数据</div>
      </div>
      <div v-else class="editor" v-loading="loading">
        <component
          :is="editorType"
          height="calc(100% - 20px)"

          :code="info.value"
          :language="info.type"
          @cursor-position="Object.assign(monaco.cursorPosition, $event)"
          @change="change = $event"
          @save="save"

          :original_value="originalValue"
          :original_language="originalLanguage"
          :modified_value="modifiedValue"
          :modified_language="modifiedLanguage"
        ></component>
        <div class="footer">
          <div type="right">行 {{ monaco.cursorPosition.lineNumber }}，列 {{ monaco.cursorPosition.column }}</div>
          <div type="right" operate=true>{{ info.type }}</div>
          <div type="right" operate=true>最后更新人：{{ info.updateUser || '未知' }}</div>
          <div type="right" operate=true>最后更新时间：{{ info.updateTime }}</div>
          <div type="left" operate=true @click.stop="getHistory(); showHistory = true; bindCancle();">
            <span style="color: #fff;font-size: 12px;line-height: 20px;display: block;">历史版本</span>
            <ul class="pop" v-if="showHistory" ref="history">
              <li
                v-for="item of history"
                :key="item._id"
                @click.stop="diffHistory(item)"
                :status="$route.query.historyId === item._id ? 'selected' : ''"
              >【{{ item.updateUser }}】{{ item.updateTime }}</li>
            </ul>
          </div>
          <div v-if="diff" type="left" operate=true @click="exitDiff">退出对比</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
module.exports = {
  components: { 'monaco': 'url:./monaco.vue', 'monaco-diff': 'url:./monaco-diff.vue' },
  data: function () {
    window.app.dc = this.$route.query.dc;
    return {
      // 导航
      breadcrumb: [],
      // 文本是否发生变更
      change: false,
      // 加载遮罩
      loading: true,
      // 子key列表
      list: [],
      // 子key信息
      info: {},
      showHistory: false,
      // 历史记录
      history: [],
      // 是否开启对比
      diff: false,
      originalValue: '',
      originalLanguage: '',
      modifiedValue: '',
      modifiedLanguage: '',
      // 编辑器相关
      monaco: {
        // 光标位置
        cursorPosition: { lineNumber: 1, column: 1 },
        setValue: null,
      }
    };
  },
  mounted() {
    const query = this.$route.query;
    this.breadcrumb = this.parseQuery(query);
    this.diff = query.diff === 'true';
  },
  computed: {
    isDir() {
      if (this.breadcrumb.length < 1) {
        return false;
      }
      return this.breadcrumb[this.breadcrumb.length - 1].type === 'dir';
    },
    targetKey() {
      return this.breadcrumb.map((item) => item.key).join('/');
    },
    editorType() {
      return this.diff ? 'monaco-diff' : 'monaco';
    }
  },
  methods: {
    bindCancle() {
      const handle = () => {
        this.showHistory = false;
        document.removeEventListener('click', handle);
      };
      document.addEventListener('click', handle);
    },
    push(item) {
      this.breadcrumb.push(item);
      this.loading = true;
    },
    createKey() {
      this.$router.push({
        path: '/create-kv',
        query: { dc: window.app.dc, prefixKey: this.targetKey },
      });
    },
    async getHistory() {
      const { data } = await axios.get('/consul/get-history', { params: { kvId: this.info._id } });
      if (!data.success) {
        return;
      }
      this.history = data.list;
      if (this.history.length === 0) {
        this.history.push({
          updateUser: '无数据',
          disableDiff: true,
        });
      }
    },
    diffHistory(history) {
      this.showHistory = false;
      if (history.disableDiff) {
        return;
      }
      this.diff = true;
      this.originalValue = history.oldValue;
      this.originalLanguage = 'json';
      this.modifiedValue = history.newValue;
      this.modifiedLanguage = 'json';
      this.$router.push({
        path: '/kv',
        query: { ...this.$route.query, diff: true, historyId: history._id },
      });
    },
    exitDiff() {
      this.diff = false;
      this.$router.push({
        path: '/kv',
        query: { ...this.$route.query, historyId: '', diff: false },
      });
    },
    async delKey(item) {
      const isDir = this.isDir;
      const dc = window.app.dc;
      await this.$confirm('确定删除吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      });
      const { data } = await axios.delete('/consul/delete-key', {
        params: {
          dc,
          key: `${ this.targetKey }/${ item.key }`,
          isDir,
          modifyIndex: item.modifyIndex,
        }
      });
      if (!data.success) {
        return;
      }
      this.$message.success('已删除！');
      if (!isDir) {
        this.breadcrumb.pop();
      } else {
        this.updateView();
      }
      this.loading = true;
    },
    async pop(index) {
      if (this.change) {
        try {
          await this.$confirm('此操作将放弃修改, 是否继续?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          });
        } catch (error) {
          return;
        }
      }
      this.diff = false;
      if (this.breadcrumb.length - 1 === index) {
        this.updateView();
      }
      while (this.breadcrumb.length - 1 > index) {
        this.breadcrumb.pop();
      }
      this.loading = true;
    },
    async updateView() {
      if (this.isDir) {
        const { data } = await axios.get('/consul/get-child-key', {
          params: {
            parentKey: this.targetKey,
            dc: window.app.dc,
          }
        });
        if (!data || !data.success || !Array.isArray(data.list)) {
          return;
        }
        const tempList = [];
        for (const item of data.list) {
          // dir类型的key最后会带一个'/'，分割的时候会在最后多一个空字符串数据，需要去掉
          const layer = item.key.split('/').filter(item => item);

          if (/\/$/.test(item.key)) {
            // 此条件分支处理key为dir类型的，最后一个字符为'/'，例如 'a/b/'

            if (layer.length === this.breadcrumb.length) {
              // 去除掉targetKey本身，例如targetKey为'a'，此条件下的key为'a/'
              continue;
            }
            if (layer.length === this.breadcrumb.length + 1) {
              // targetKey下dir类型的key，例如targetKey为'a'，此条件下的key可能为 'a/b/'、'a/c/' 等
              tempList.push({ ...item, key: item.key.replace(/\/$/, ''), type: 'dir' });
              continue;
            }
            // targetKey下dir类型的key，但是为更深层次的key，例如targetKey为'a'，此条件下的key可能为 'a/b/c1'、'a/b/c2' 等
            tempList.push({ ...item, key: layer.slice(0, this.breadcrumb.length + 1).join('/'), type: 'dir' });
          } else {
            // 此条件分支处理key为非dir类型的，例如 'a/b'、'a/c'等

            if (layer.length === this.breadcrumb.length + 1) {
              // targetKey下非dir的key，例如targetKey为'a'，此条件下的key可能为 'a/c1'、'a/c2' 等
              tempList.push({ ...item, key: item.key, type: item.type });
              continue;
            }
            const parentKey = item.key.split('/').slice(0, this.breadcrumb.length + 1).join('/');
            tempList.push({ ...item, key: parentKey, type: 'dir' });
          }
        }
        const list = [];
        for (const tempItem of tempList) {
          let has = false;
          for (const item of list) {
            if (item.key === tempItem.key) {
              has = true;
            }
          }
          if (!has) {
            list.push(tempItem);
          }
        }
        list.forEach((item) => {
          item.key = item.key.split('/')[this.breadcrumb.length];
        });
        this.list = list.sort((a, b) => {
          // 类型相同时按字符排序，不同时dir在前
          return a.type === b.type ? (a.key > b.key ? 1 : -1) : (a.type === 'dir' ? -1 : 1);
        });
      } else {
        const { data } = await axios.get('/consul/get-value', {
          params: {
            key: this.targetKey,
            dc: window.app.dc,
          }
        });
        if (!data.info) {
          this.breadcrumb.pop();
          return;
        }
        this.info = data.info;
          debugger;
        if (this.$route.query.historyId) {
          await this.getHistory();
          for (const item of this.history) {
            if (item._id === this.$route.query.historyId) {
              this.diffHistory(item);
            }
          }
        }
      }
      this.loading = false;
    },
    parseQuery(query) {
      const { key = 'online-school', type = 'dir' } = query;
      const keyList = key.split('/');
      const targetKey = keyList.pop();
      const breadcrumb = [];
      for (const key of keyList) {
        breadcrumb.push({ key, type: 'dir' });
      }
      breadcrumb.push({ key: targetKey, type });
      return breadcrumb;
    },
    async save(value) {
      const { data } = await axios.put('/consul/update-key', {
        _id: this.info._id,
        modifyIndex: this.info.modifyIndex,
        dc: window.app.dc,
        action: 'update',
        value,
      });
      if (data.success) {
        this.info = data.info;
        this.$message.success('更新成功！');
      }
    },
  },
  beforeRouteUpdate(to, from, next) {
    window.app.dc = to.query.dc;
    this.diff = to.query.diff === 'true';
    // 从路由中定位
    const breadcrumb = this.parseQuery(to.query);
    if (from.query.dc !== to.query.dc) {
      this.breadcrumb = breadcrumb;
      return next();
    }
    // 由于这里需要改变一次breadcrumb，必然会触发watch事件，所以手动检验实际有无发生变化
    if (breadcrumb.length !== this.breadcrumb.length) {
      this.breadcrumb = breadcrumb;
      return next();
    }
    for (let i = 0; i < breadcrumb.length; i ++) {
      for (const key in breadcrumb[i]) {
        if (this.breadcrumb[i][key] !== breadcrumb[i][key]) {
          this.breadcrumb = breadcrumb;
          return next();
        }
      }
    }
    next();
  },
  watch: {
    async breadcrumb() {
      this.$router.push({
        path: '/kv',
        query: {
          ...this.$route.query,
          dc: window.app.dc,
          key: this.targetKey,
          type: this.breadcrumb[this.breadcrumb.length - 1].type,
          diff: this.diff,
        }
      });
      await this.updateView();
    },
  },
};
</script>

<style scoped>
.breadcrumb {
  height: var(--breadcrumb-height);
  border-bottom: 1px #eee solid;
  line-height: var(--breadcrumb-height);
}

.breadcrumb [name="keyName"] {
  display: inline;
}

.breadcrumb span {
  color: #585858;
  font-weight: 600;
  font-size: 16px;
  transition: 0.2s;
  cursor: pointer;
}

.breadcrumb span:hover {
  color: #454545;
}

.item {
  border-bottom: 1px solid rgb(238, 238, 238);
  margin: 0px;
  line-height: 50px;
  height: 50px;
  padding-left: 10px;
  cursor: pointer;
  transition: 0.3s;
  background: #fff;
  font-size: 15px;
}

.item:hover {
  background: #f4f4f4;
}

.dir {
  padding-right: 3px;
}

.dir:after {
  content: "\e78a";
  color: #b4b4b4;
  font-family: element-icons!important;
}

.json {
  padding-right: 3px;
}

.json:after {
  content: "{ }";
  color: #b4b4b4;
}

.string {
  padding-right: 3px;
}

.string:after {
  content: "str";
  color: #b4b4b4;
}

.box {
  overflow: auto;
  height: calc(100vh - var(--breadcrumb-height) - 2px - var(--nav-height));
}

.editor {
  height: calc(100vh - var(--breadcrumb-height) - 5px - var(--nav-height));
  overflow: hidden;
}

.footer {
  height: 20px;
  background: var(--theme-color);
  color: #fff;
  font-size: 12px;
  line-height: 20px;
  padding: 0 10px;
  cursor: default;
  user-select: none;
}

.footer [operate=true] {
  cursor: pointer;
  transition: .2s;
}

.footer [operate=true]:hover {
  background: #b31c62;
}

.footer [type=right] {
  float: right;
  padding: 0 8px;
}

.footer [type=left] {
  float: left;
  padding: 0 8px;
}

.del {
  float: right;
  line-height: 24px;
  margin-right: 15px;
  height: 24px;
  width: 24px;
  text-align: center;
  margin-top: 13px;
  color: #898989;
  transition: .3s;
  cursor: pointer;
}

.del:hover {
  background: #ccc;
}

.create {
  float: right;
  background: var(--theme-color);
  height: 30px;
  line-height: 30px;
  margin-top: 10px;
  padding: 0 10px;
  border-radius: 4px;
  color: #fff;
  font-size: 14px;
  cursor: pointer;
  transition: .3s;
}

.create:hover {
  background: var(--theme-color-hover);
}

.noData {
  text-align: center;
  padding-top: 20px;
  font-size: 14px;
  color: #c9c9c9;
}

.el-loading-mask {
  height: calc(100vh - var(--breadcrumb-height) - 5px - var(--nav-height));
}

.pop {
  position: absolute;
  margin: 0px;
  bottom: 30px;
  padding: 8px 0 8px 0;
  border-radius: 4px;
  max-height: 400px;
  overflow: auto;
  background: rgb(255, 255, 255);
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 20%);
}

.pop li {
  background: #fff;
  list-style: none;
  padding: 5px 15px;
  height: 25px;
  line-height: 25px;
  color: #000;
  white-space: nowrap;
}

.pop li[status="selected"] {
  color: var(--theme-color);
  font-weight: 600;
  background: var(--theme-color-background);
}

.pop li:hover {
  background: var(--theme-color-background);
  color: var(--theme-color);
}

* {
  --breadcrumb-height: 50px;
}

</style>